package tr.gen._3bit.java._2k1s.web.action;

import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.UnsupportedCommOperationException;

import java.io.IOException;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import tr.gen._3bit.java._2k1s.api._2K1SComm;
import tr.gen._3bit.java._2k1s.api.exception.DeviceIsBusyException;
import tr.gen._3bit.java._2k1s.api.exception.NotASerialPortException;
import tr.gen._3bit.java._2k1s.api.exception.QueryCorruptedDataException;
import tr.gen._3bit.java._2k1s.api.exception.QueryTimeoutException;
import tr.gen._3bit.java._2k1s.api.exception.UnknownErrorException;
import tr.gen._3bit.java._2k1s.dao.model.Measurement;
import tr.gen._3bit.java._2k1s.dao.model.Node;
import tr.gen._3bit.java._2k1s.dao.model.SerialInterface;
import tr.gen._3bit.java._2k1s.dao.service.MeasurementService;
import tr.gen._3bit.java._2k1s.web.exception._2K1SConnectionException;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.Preparable;

public class _2K1SAction extends ActionSupport implements Runnable,Preparable {

	private static final long serialVersionUID = -7098702760298537380L;
	
	private static final String SIKEY = "session_serialInterface_id";

	private static Set<_2K1SAction> threads = new HashSet<_2K1SAction>();

	private Thread _thread;
	public SerialInterface serialInterface;
	private _2K1SComm comm;

	private List<Measurement> measurements;
	
	private Integer id;
	
	private MeasurementService measurementService;
	
	public _2K1SAction(MeasurementService measurementService) {
		this.measurementService = measurementService;
	}

	public String start() throws _2K1SConnectionException {
		_2K1SAction __thread = get2K1SThread(getSerialInterface()); 
		if (__thread == null) 
			__thread = this;
		
		try {
			__thread.commConnect();
		} catch (Exception e) {
			addActionError(e.getClass().getSimpleName() + ": " + e.getMessage());
			e.printStackTrace();
			__thread = null;
			throw new _2K1SConnectionException(e);
		}
		
		__thread._t_start();
		threads.add(__thread);
		addActionMessage(getText("_2k1saction.messages.start.successfull"));
		return execute();
	}
	
	public String stop() throws _2K1SConnectionException {
		_2K1SAction __thread = get2K1SThread(getSerialInterface());
		if (__thread == null) {
			addActionMessage(getText("_2k1saction.messages.stop.noSuchThread"));
			return execute();
		}
		
		__thread._t_stop();
		try {
			__thread.commDisconnect();
		} catch (IOException e) {
			addActionError(e.getMessage());
			e.printStackTrace();
			throw new _2K1SConnectionException(e);
		}
		threads.remove(__thread);
		addActionMessage(getText("_2k1saction.messages.stop.successfull"));
		return execute();
	}
	
	public String status() {
		_2K1SAction __thread = get2K1SThread(getSerialInterface());
		if (__thread == null || !__thread._t_status())
			addActionMessage(getText("_2k1saction.messages.status.stopped"));
		else 
			addActionMessage(getText("_2k1saction.messages.status.started"));
		return execute();
	}
	
	public String list() {
		this.measurements = getMeasurementService().findAll();
		return execute();
	}
	
	public String remove() {
		getMeasurementService().remove(id);
		return execute();
	}
	@SuppressWarnings("unchecked")
	public String menu() {
		if (serialInterface != null)
			ActionContext.getContext().getSession().put(SIKEY, serialInterface);
		return execute();
	}
	public void run() {
		Thread thisThread = Thread.currentThread();
		while (_thread == thisThread) {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//logic
			for (Node node : getSerialInterface().getNodes())
				if (isOverTime(node, getMeasurementService()
						.getLastByNode(node))) {
					Measurement _mes = new Measurement();
					_mes.setDate(new Date());
					_mes.setNode(node);
					_mes.setSerialInterface(getSerialInterface());
					if (comm.isConnected())
						try {
							_mes.setTemperature(comm.queryTemperature(node
									.getIdentCode()));
							_mes.setReturnCode(0);
						} catch (IOException e) {
							_mes.setMessage(e.getMessage());
							_mes.setReturnCode(1);
						} catch (DeviceIsBusyException e) {
							_mes.setMessage(e.getMessage());
							_mes.setReturnCode(2);
						} catch (QueryCorruptedDataException e) {
							_mes.setMessage(e.getMessage());
							_mes.setReturnCode(3);
						} catch (QueryTimeoutException e) {
							_mes.setMessage(e.getMessage());
							_mes.setReturnCode(100);
						}
					else {
						_mes.setMessage("Not Connected, please see logs");
						_mes.setReturnCode(4);
					}
					getMeasurementService().save(_mes);
				}

		}
	}

	private Boolean _t_status() {
		return _thread != null;
	}

	private void _t_start() {
		if (_thread != null)
			return;
		_thread = new Thread(this);
		_thread.setPriority(Thread.MIN_PRIORITY);
		_thread.start();
	}

	private void _t_stop() {
		if (_thread == null)
			return;
		Thread __thread = _thread;
		_thread = null;
		__thread.interrupt();
	}

	private MeasurementService getMeasurementService() {
		return measurementService;
	}

	public SerialInterface getSerialInterface() {
		return serialInterface;
	}

	public void setSerialInterface(SerialInterface serialInterface) {
		this.serialInterface = serialInterface;
	}

	private _2K1SAction get2K1SThread(SerialInterface serialInterface) {
		for (_2K1SAction thread : threads)
			if (thread.serialInterface.equals(serialInterface))
				return thread;
		return null;
	}

	private Boolean isOverTime(Node node, Measurement measurement) {
		return new Date().getTime() - measurement.getDate().getTime() > new Long(
				1000)
				* (long) node.getTimeInterval();
	}

	private void commConnect() throws NoSuchPortException, PortInUseException,
			UnsupportedCommOperationException, IOException,
			UnknownErrorException, NotASerialPortException {
		if (comm == null)
			comm = new _2K1SComm(getSerialInterface());
		if (!comm.isConnected())
			comm.connect();
	}

	private void commDisconnect() throws IOException {
		if (comm != null && comm.isConnected())
			comm.disconnect();
		comm = null;
	}

	public List<Measurement> getMeasurements() {
		return measurements;
	}

	public void setMeasurements(List<Measurement> measurements) {
		this.measurements = measurements;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String execute() {
		return Action.SUCCESS;
	}

	@Override
	public void prepare() throws Exception {
		Object si = ActionContext.getContext().getSession().get(SIKEY);
		if (si != null && si instanceof SerialInterface)
			setSerialInterface((SerialInterface) si);
	}
	
}
